// 前台写死路由的引入方法
// import { asyncRoutes, constantRoutes } from '@/router'
// 后台引入路由的写法
import { constantRoutes } from '@/router'
import { getMenuList } from '@/api/user'
import Layout from '@/layout'
// import routerData from '../route'
// 这个文件中封装了两个动态路由的写法
// 第一种：后台反回角色或者身份，路由表前台写死，然后根据角色显示对应的路由
//    这种方式，因为路由是从自己的路由表中直接获取的，所以组件信息都还在，不用重新改变
//    只需要做一个循环就可以了
// 第二种：后台返回整个的路由表，前台根据后台数据重新生成路由表
//    这种方式由于没有组件引用的写法，组件不具备自己引入的功能，就需要自己根据
//    后台反回的字段，重新生成组件的对应引入信息，写法会比较复杂
/**
 * Use meta.role to determine if the current user has permission
 * @param roles 类型就是数组['sys:edit','ays:search']，是当前登录人能看到的所有路由的集合
 * @param route 传递过来的路由的格式
 *         {
            "path": "/system",
            "component": "Layout",
            "alwaysShow": true,
            "name": "system",
            "meta": {
                "title": "系统管理",
                "icon": "component",
                "roles": [
                    "sys:manage"
                ]
            },
 */
function hasPermission (roles, route) {
  // 如果这个路由有meta元信息，而且有角色这个字段
  if (route.meta && route.meta.roles) {
    // some 如果有一个元素满足条件，则表达式返回true , 剩余的元素不会再执行检测 否则返回false
    // includes 如果找到匹配的字符串则返回 true，否则返回 false
    // 循环整个的角色信息，只要角色信息中的任何一个，等于路由信息角色中的字段，这个路由就符合标准
    // 这里之所以用路由中的信息包含单个，是因为，角色信息是一个数组，便于使用数组的方法
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 * @param roles
 * 注意这里的递归遍历是把路由处理成了一个固定的样式,这个样式
 * 一定要是跟后端协商好的,固定格式的,否则前端就需要去做大量的
 * 处理,这样会导致工作量十分的巨大
 * 这个roles在最外层的permission.js中通过vuex获取
 * const { roles } = await store.dispatch('user/getInfo')
 * 类型大概就是['sys:edit','ays:search']
 */
// 后台返回数据的路由的过滤方法
export function filterAsyncRoutes (routes, roles) {
  const res = []
  // 遍历每个路由，这里的routes是由后面的请求函数发送过来的
  routes.forEach(route => {
    const tmp = { ...route }
    /**
     * 这里对route使用深复制，就是为了防止对原始数据的篡改，避免数据的相互影响
     * 如果不太确定这个tmp是什么就可以打印它看一下
     * {path: '/userList', component: '/system/User/UserList', alwaysShow: false, name: 'userList', meta: {…}}
     * 它其实是一个整个的路由信息
     */
    // console.log('这里是准备循环的路由', tmp)
    // 判断它是否拥有roles这个权限，如果有，证明当前登录人可以看到这个路由，就下面继续
    // 这个判断的前提是后台返回了整个的路由表，需要用权限进行一下判断
    // 目前这个接口,后台返回的就是已经过滤后的路由了,就不需要了
    // 其实这个判断可以省略(这个hasPermission的函数判断只有在后台返回全部的路由节点的时候才需要进行判断)
    // 自己使用假数据进行测试的时候也证明了这一点，就是使用全量的路由表，不加判断会返回整个的路由列表
    // 加上判断就会只显示自己能看到的列表，这里就可以根据后台返回的数据类型，就是角色来判断他能不能看到更多的东西
    // 这个可以用来判断自己写的路由表，然后把这个路由表整个循环一遍就可以
    if (hasPermission(roles, tmp)) {
      const component = tmp.component
      // 增加对路由的component的处理
      if (route.component) {
        if (component === 'Layout') {
          tmp.component = Layout
        } else {
          // 这里是子级路由的加载方式，参考动态路由的方法
          tmp.component = resolve =>
            require([`@/views${component}.vue`], resolve)
        }
      }
      // 判断是否有下级,这是一个递归
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      // 如果有权限,就添加到res中
      res.push(tmp)
    }
  })
  // 这样遍历之后，剩下的就是拥有这个权限的路由和它的子路由
  // 经过这个处理，生成的路由就是符合需要的动态的路由
  return res
}

// 前台写死路由的过滤方法，这两种方法的区别在于component的写法，主要有一个引入的问题
export function filterAsyncRoutes1 (routes, roles) {
  const res = []

  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes1(tmp.children, roles)
      }
      res.push(tmp)
    }
  })

  return res
}

const state = {
  // 最终的路由表，就是动态的和固定的相加的路由表
  routes: [],
  // 动态的路由表
  addRoutes: []
}

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    // 把过滤出来的有权限的路由添加到没有权限的路由中去
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes ({ commit }, roles) {
    return new Promise(resolve => {
      // 存的是有权限的路由,是一个数组
      let accessedRoutes
      // 获取菜单数据
      getMenuList()
        .then(response => {
          // console.log('获取的菜单数据', response)
          // 这个roles在最外层的permission.js中通过vuex获取
          // const { roles } = await store.dispatch('user/getInfo')
          // 类型大概就是['sys:edit','ays:search']
          accessedRoutes = filterAsyncRoutes(response.data, roles)
          // 前台写死路由的使用方法
          // accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
          // 存储菜单数据到store
          commit('SET_ROUTES', accessedRoutes)
          resolve(accessedRoutes)
        })
        .catch(err => {
          console.log(err)
        })
      // 如果roles中含有admin字样,他就拥有所有的权限
      // if (roles.includes('admin')) {
      //   accessedRoutes = asyncRoutes || []
      // } else {
      //   // 如果不是,就需要处理,调用filterAsyncRoutes方法,在本页封装的方法
      //   accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      // }
      // commit('SET_ROUTES', accessedRoutes)
      // resolve(accessedRoutes)
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}
